/*
 * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE
 * file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file
 * to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the
 * License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */
package com.swak.monitor.dubbo.service.impl;

import java.util.List;

import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;

import com.swak.monitor.dubbo.exception.ResourceNotFoundException;
import com.swak.monitor.dubbo.model.dto.AccessDTO;
import com.swak.monitor.dubbo.model.dto.ConditionRouteDTO;
import com.swak.monitor.dubbo.model.dto.ConditionRouteResultDTO;
import com.swak.monitor.dubbo.model.dto.TagRouteDTO;
import com.swak.monitor.dubbo.model.store.RoutingRule;
import com.swak.monitor.dubbo.model.store.TagRoute;
import com.swak.monitor.dubbo.service.RouteService;
import com.swak.monitor.dubbo.utils.Constants;
import com.swak.monitor.dubbo.utils.ConvertUtil;
import com.swak.monitor.dubbo.utils.RouteUtils;
import com.swak.monitor.dubbo.utils.YamlParser;
import com.swak.utils.StringUtils;

@Component
public class RouteServiceImpl extends AbstractService implements RouteService {

	private String prefix = Constants.CONFIG_KEY;

	@Override
	public void createConditionRoute(ConditionRouteDTO conditionRoute, String serviceVersion, String serviceGroup) {
		String id = ConvertUtil.getIdFromDTO(conditionRoute, serviceVersion, serviceGroup);
		String path = getPath(id, Constants.CONDITION_ROUTE);
		String existConfig = dynamicConfiguration.getConfig(path);
		RoutingRule existRule = null;
		if (existConfig != null) {
			existRule = YamlParser.loadObject(existConfig, RoutingRule.class);
		}
		existRule = RouteUtils.insertConditionRule(existRule, conditionRoute);
		// register2.7
		dynamicConfiguration.setConfig(path, YamlParser.dumpObject(existRule));

		// register2.6
		// if (StringUtils.isNotEmpty(conditionRoute.getService())) {
		// for (Route old : convertRouteToOldRoute(conditionRoute)) {
		// registry.register(old.toUrl().addParameter(Constants.COMPATIBLE_CONFIG,
		// true));
		// }
		// }
	}

	@Override
	public void updateConditionRoute(ConditionRouteDTO newConditionRoute, String serviceVersion, String serviceGroup) {
		String id = ConvertUtil.getIdFromDTO(newConditionRoute, serviceVersion, serviceGroup);
		String path = getPath(id, Constants.CONDITION_ROUTE);
		String existConfig = dynamicConfiguration.getConfig(path);
		if (existConfig == null) {
			throw new ResourceNotFoundException("no existing condition route for path: " + path);
		}
		RoutingRule routingRule = YamlParser.loadObject(existConfig, RoutingRule.class);
		// ConditionRouteDTO oldConditionRoute =
		// RouteUtils.createConditionRouteFromRule(routingRule);
		routingRule = RouteUtils.insertConditionRule(routingRule, newConditionRoute);
		dynamicConfiguration.setConfig(path, YamlParser.dumpObject(routingRule));

		// for 2.6
		// if (StringUtils.isNotEmpty(newConditionRoute.getService())) {
		// for (Route old : convertRouteToOldRoute(oldConditionRoute)) {
		// registry.unregister(old.toUrl().addParameter(Constants.COMPATIBLE_CONFIG,
		// true));
		// }
		// for (Route updated : convertRouteToOldRoute(newConditionRoute)) {
		// registry.register(updated.toUrl().addParameter(Constants.COMPATIBLE_CONFIG,
		// true));
		// }
		// }
	}

	@Override
	public void deleteConditionRoute(ConditionRouteDTO conditionRoute) {
		String id = ConvertUtil.getIdFromDTO(conditionRoute);
		String path = getPath(id, Constants.CONDITION_ROUTE);

		String config = dynamicConfiguration.getConfig(path);
		if (config == null) {
			// throw exception
		}
		RoutingRule route = YamlParser.loadObject(config, RoutingRule.class);
		List<String> blackWhiteList = RouteUtils.filterBlackWhiteListFromConditions(route.getConditions());
		if (blackWhiteList.size() != 0) {
			route.setConditions(blackWhiteList);
			dynamicConfiguration.setConfig(path, YamlParser.dumpObject(route));
		} else {
			dynamicConfiguration.deleteConfig(path);
		}

		// for 2.6
//		if (route.getScope().equals(Constants.SERVICE)) {
//			RoutingRule originRule = YamlParser.loadObject(config, RoutingRule.class);
//			ConditionRouteDTO conditionRouteDTO = RouteUtils.createConditionRouteFromRule(originRule);
//			for (Route old : convertRouteToOldRoute(conditionRouteDTO)) {
//				registry.unregister(old.toUrl().addParameter(Constants.COMPATIBLE_CONFIG, true));
//			}
//		}
	}

	@Override
	public void deleteAccess(String id) {
		String path = getPath(id, Constants.CONDITION_ROUTE);
		String config = dynamicConfiguration.getConfig(path);
		if (config != null) {
			RoutingRule ruleDTO = YamlParser.loadObject(config, RoutingRule.class);
			// List<String> blackWhiteList =
			// RouteUtils.filterBlackWhiteListFromConditions(ruleDTO.getConditions());
			List<String> conditions = RouteUtils.filterConditionRuleFromConditions(ruleDTO.getConditions());
			if (conditions.size() == 0) {
				dynamicConfiguration.deleteConfig(path);
			} else {
				ruleDTO.setConditions(conditions);
				dynamicConfiguration.setConfig(path, YamlParser.dumpObject(ruleDTO));
			}
			// 2.6
//			if (ruleDTO.getScope().equals(Constants.SERVICE) && blackWhiteList.size() > 0) {
//				Route route = RouteUtils.convertBlackWhiteListtoRoute(blackWhiteList, Constants.SERVICE, id);
//				registry.unregister(route.toUrl());
//			}
		}
	}

	@Override
	public void createAccess(AccessDTO accessDTO) {
		String id = ConvertUtil.getIdFromDTO(accessDTO);
		String path = getPath(id, Constants.CONDITION_ROUTE);
		String config = dynamicConfiguration.getConfig(path);
		List<String> blackWhiteList = RouteUtils.convertToBlackWhiteList(accessDTO);
		RoutingRule ruleDTO;
		if (config == null) {
			ruleDTO = new RoutingRule();
			ruleDTO.setEnabled(true);
			if (StringUtils.isNoneEmpty(accessDTO.getApplication())) {
				ruleDTO.setScope(Constants.APPLICATION);
			} else {
				ruleDTO.setScope(Constants.SERVICE);
			}
			ruleDTO.setKey(id);
			ruleDTO.setConditions(blackWhiteList);
		} else {
			ruleDTO = YamlParser.loadObject(config, RoutingRule.class);
			if (ruleDTO.getConditions() == null) {
				ruleDTO.setConditions(blackWhiteList);
			} else {
				ruleDTO.getConditions().addAll(blackWhiteList);
			}
		}
		dynamicConfiguration.setConfig(path, YamlParser.dumpObject(ruleDTO));

		// for 2.6
//		if (ruleDTO.getScope().equals("service")) {
//			Route route = RouteUtils.convertAccessDTOtoRoute(accessDTO);
//			registry.register(route.toUrl());
//		}

	}

	@Override
	public AccessDTO findAccess(String id) {
		String path = getPath(id, Constants.CONDITION_ROUTE);
		String config = dynamicConfiguration.getConfig(path);
		if (config != null) {
			RoutingRule ruleDTO = YamlParser.loadObject(config, RoutingRule.class);
			List<String> blackWhiteList = RouteUtils.filterBlackWhiteListFromConditions(ruleDTO.getConditions());
			return RouteUtils.convertToAccessDTO(blackWhiteList, ruleDTO.getScope(), ruleDTO.getKey());
		}
		return null;
	}

	@Override
	public void updateAccess(AccessDTO accessDTO) {
		String key = ConvertUtil.getIdFromDTO(accessDTO);
		String path = getPath(key, Constants.CONDITION_ROUTE);
		List<String> blackWhiteList = RouteUtils.convertToBlackWhiteList(accessDTO);
		String config = dynamicConfiguration.getConfig(path);
		// List<String> oldList = null;
		if (config != null) {
			RoutingRule ruleDTO = YamlParser.loadObject(config, RoutingRule.class);
			// oldList =
			// RouteUtils.filterBlackWhiteListFromConditions(ruleDTO.getConditions());
			List<String> conditions = RouteUtils.filterConditionRuleFromConditions(ruleDTO.getConditions());
			conditions.addAll(blackWhiteList);
			ruleDTO.setConditions(conditions);
			dynamicConfiguration.setConfig(path, YamlParser.dumpObject(ruleDTO));
		}

		// 2.6
//		if (StringUtils.isNotEmpty(accessDTO.getService())) {
//			Route oldRoute = RouteUtils.convertBlackWhiteListtoRoute(oldList, Constants.SERVICE, key);
//			Route newRoute = RouteUtils.convertAccessDTOtoRoute(accessDTO);
//			registry.unregister(oldRoute.toUrl());
//			registry.register(newRoute.toUrl());
//		}
	}

	@Override
	public void enableConditionRoute(ConditionRouteDTO conditionRoute) {
		String id = ConvertUtil.getIdFromDTO(conditionRoute);
		String path = getPath(id, Constants.CONDITION_ROUTE);

		String config = dynamicConfiguration.getConfig(path);
		if (config != null) {
			RoutingRule ruleDTO = YamlParser.loadObject(config, RoutingRule.class);

			if (ruleDTO.getScope().equals(Constants.SERVICE)) {
				// for2.6
//				for (Route oldRoute : convertRouteToOldRoute(RouteUtils.createConditionRouteFromRule(ruleDTO))) {
//					URL oldURL = oldRoute.toUrl();
//					registry.unregister(oldURL);
//					oldURL = oldURL.addParameter("enabled", true);
//					registry.register(oldURL);
//				}
			}

			// 2.7
			ruleDTO.setEnabled(true);
			dynamicConfiguration.setConfig(path, YamlParser.dumpObject(ruleDTO));
		}

	}

	@Override
	public void disableConditionRoute(ConditionRouteDTO conditionRoute) {
		String id = ConvertUtil.getIdFromDTO(conditionRoute);
		String path = getPath(id, Constants.CONDITION_ROUTE);

		String config = dynamicConfiguration.getConfig(path);
		if (config != null) {
			RoutingRule routeRule = YamlParser.loadObject(config, RoutingRule.class);

			if (routeRule.getScope().equals(Constants.SERVICE)) {
				// for 2.6
//				for (Route oldRoute : convertRouteToOldRoute(RouteUtils.createConditionRouteFromRule(routeRule))) {
//					URL oldURL = oldRoute.toUrl();
//					registry.unregister(oldURL);
//					oldURL = oldURL.addParameter("enabled", false);
//					registry.register(oldURL);
//				}
			}

			// 2.7
			routeRule.setEnabled(false);
			dynamicConfiguration.setConfig(path, YamlParser.dumpObject(routeRule));
		}

	}

	@Override
	public ConditionRouteResultDTO findConditionRoute(ConditionRouteDTO crDTO) {
		return findConditionRoute(ConvertUtil.getIdFromDTO(crDTO));
	}

	@Override
	public ConditionRouteResultDTO findConditionRoute(String id) {
		String path = getPath(id, Constants.CONDITION_ROUTE);
		String config = dynamicConfiguration.getConfig(path);
		if (config != null) {
			RoutingRule routingRule = YamlParser.loadObject(config, RoutingRule.class);
			ConditionRouteDTO conditionRouteDTO = RouteUtils.createConditionRouteFromRule(routingRule);
			String service = conditionRouteDTO.getService();
			if (StringUtils.isNotBlank(service)) {
				conditionRouteDTO.setService(service.replace("*", "/"));
			}
			ConditionRouteResultDTO result = new ConditionRouteResultDTO();
			BeanUtils.copyProperties(conditionRouteDTO, result);
			result.setScope(routingRule.getScope());
			String[] detachResult = ConvertUtil.detachId(id);
			if (detachResult.length > 1) {
				result.setServiceVersion(detachResult[1]);
			}
			if (detachResult.length > 2) {
				result.setServiceGroup(detachResult[2]);
			}
			return result;
		}
		return null;
	}

	@Override
	public void createTagRoute(TagRouteDTO tagRoute) {
		String id = ConvertUtil.getIdFromDTO(tagRoute);
		String path = getPath(id, Constants.TAG_ROUTE);
		TagRoute store = RouteUtils.convertTagroutetoStore(tagRoute);
		dynamicConfiguration.setConfig(path, YamlParser.dumpObject(store));
	}

	@Override
	public void updateTagRoute(TagRouteDTO tagRoute) {
		String id = ConvertUtil.getIdFromDTO(tagRoute);
		String path = getPath(id, Constants.TAG_ROUTE);
		if (dynamicConfiguration.getConfig(path) == null) {
			throw new ResourceNotFoundException("can not find tagroute: " + id);
			// throw exception
		}
		TagRoute store = RouteUtils.convertTagroutetoStore(tagRoute);
		dynamicConfiguration.setConfig(path, YamlParser.dumpObject(store));

	}

	@Override
	public void deleteTagRoute(String id) {
		String path = getPath(id, Constants.TAG_ROUTE);
		dynamicConfiguration.deleteConfig(path);
	}

	@Override
	public void enableTagRoute(String id) {
		String path = getPath(id, Constants.TAG_ROUTE);
		String config = dynamicConfiguration.getConfig(path);
		if (config != null) {
			TagRoute tagRoute = YamlParser.loadObject(config, TagRoute.class);
			tagRoute.setEnabled(true);
			dynamicConfiguration.setConfig(path, YamlParser.dumpObject(tagRoute));
		}

	}

	@Override
	public void disableTagRoute(String id) {
		String path = getPath(id, Constants.TAG_ROUTE);
		String config = dynamicConfiguration.getConfig(path);
		if (config != null) {
			TagRoute tagRoute = YamlParser.loadObject(config, TagRoute.class);
			tagRoute.setEnabled(false);
			dynamicConfiguration.setConfig(path, YamlParser.dumpObject(tagRoute));
		}

	}

	@Override
	public TagRouteDTO findTagRoute(String id) {
		String path = getPath(id, Constants.TAG_ROUTE);
		String config = dynamicConfiguration.getConfig(path);
		if (config != null) {
			TagRoute tagRoute = YamlParser.loadObject(config, TagRoute.class);
			return RouteUtils.convertTagroutetoDisplay(tagRoute);
		}
		return null;
	}

	private String getPath(String key, String type) {
		key = key.replace("/", "*");
		if (type.equals(Constants.CONDITION_ROUTE)) {
			return prefix + Constants.PATH_SEPARATOR + key + Constants.CONDITION_RULE_SUFFIX;
		} else {
			return prefix + Constants.PATH_SEPARATOR + key + Constants.TAG_RULE_SUFFIX;
		}
	}

//	private String parseCondition(String condition) {
//		StringBuilder when = new StringBuilder();
//		StringBuilder then = new StringBuilder();
//		condition = condition.trim();
//		if (condition.contains("=>")) {
//			String[] array = condition.split("=>", 2);
//			String consumer = array[0].trim();
//			String provider = array[1].trim();
//			if (consumer.length() != 0) {
//				if (when.length() != 0) {
//					when.append(" & ").append(consumer);
//				} else {
//					when.append(consumer);
//				}
//			}
//			if (provider.length() != 0) {
//				if (then.length() != 0) {
//					then.append(" & ").append(provider);
//				} else {
//					then.append(provider);
//				}
//			}
//		}
//		return (when.append(" => ").append(then)).toString();
//	}

//	private List<Route> convertRouteToOldRoute(ConditionRouteDTO route) {
//		List<Route> oldList = new LinkedList<Route>();
//		for (String condition : route.getConditions()) {
//			Route old = new Route();
//			old.setService(route.getService());
//			old.setEnabled(route.isEnabled());
//			old.setForce(route.isForce());
//			old.setRuntime(route.isRuntime());
//			old.setPriority(route.getPriority());
//			String rule = parseCondition(condition);
//			old.setRule(rule);
//			oldList.add(old);
//		}
//		return oldList;
//	}
}
